import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:base_plugin/base_plugin.dart';
import 'package:connectivity/connectivity.dart';
import 'package:dio/dio.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/mock/mock_server.dart';
import 'package:finance_app/services/event_bus.dart';
import 'package:finance_app/src/base_plugin/base_dialog.dart';
import 'package:finance_app/src/base_plugin/base_entity.dart';
import 'package:finance_app/src/base_plugin/base_page.dart';
import 'package:finance_app/src/base_plugin/base_presenter.dart';
import 'package:finance_app/src/entities/biometrics_data_entity.dart';
import 'package:finance_app/src/entities/company_entity.dart';
import 'package:finance_app/src/entities/company_financial_year_entity.dart';
import 'package:finance_app/src/entities/country_entity.dart';
import 'package:finance_app/src/entities/current_subscription_entity.dart';
import 'package:finance_app/src/entities/employee_contact_entity.dart';
import 'package:finance_app/src/entities/message_count_entity.dart';
import 'package:finance_app/src/entities/my_invitations_entity.dart';
import 'package:finance_app/src/entities/payment_term_entity.dart';
import 'package:finance_app/src/entities/role_permission_entity.dart';
import 'package:finance_app/src/entities/setting_app_entity.dart';
import 'package:finance_app/src/entities/setting_data_entity.dart';
import 'package:finance_app/src/entities/setting_info_entity.dart';
import 'package:finance_app/src/entities/user_profile_entity.dart';
import 'package:finance_app/src/http_service/http_assistant_service.dart';
import 'package:finance_app/src/http_service/http_category_service.dart';
import 'package:finance_app/src/http_service/http_company_service.dart';
import 'package:finance_app/src/http_service/http_employee_contact_service.dart';
import 'package:finance_app/src/http_service/http_invitation_service.dart';
import 'package:finance_app/src/http_service/http_permission_service.dart';
import 'package:finance_app/src/http_service/http_setting_service.dart';
import 'package:finance_app/src/interceptor/login_interceptor.dart';
import 'package:finance_app/src/provider/dashboard_provider.dart';
import 'package:finance_app/src/router_gen/override_route_providers.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/third_part/push_manager.dart';
import 'package:finance_app/src/utils/form_utils.dart';
import 'package:finance_app/src/utils/permission_utils.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';

class Config {
  // http://xjp2.fangwei6.com
  // https://dev.acesoft.com.sg
  // https://api.acesoft.com.sg
  static String baseUrl = 'https://dev.acesoft.com.sg';
  // static String baseUrl = 'http://xjp2.fangwei6.com';
  static String webSocketUrl = 'wss://dev.acesoft.com.sg';

  static String appVersion = '2.1.0';

  // static String baseUrl = 'http://54.169.100.125';
  static String appName = 'Ace Accounts';

  static String privacyPolicyUrl =
      'https://privacy.acesoft.com.sg/privacy.html';
  static String termOfServiceUrl = 'https://privacy.acesoft.com.sg/terms.html';
  static String contactUsUrl = 'https://acesoft.com.sg/contact-us/';

  static EventBus eventBus;
  static OSNotification notificationToBeFired;

  // only dashboardProvider will store in memory due to route issue
  static DashboardProvider dashboardProvider;

  //这里可以设置一些整个应用需要刷新的数据进行判断
  static bool shouldRebuild(
      Map<String, dynamic> previous, Map<String, dynamic> next) {
    return previous[ConfigKey.userProfile] != next[ConfigKey.userProfile];
  }

  static Future<bool> initConfig() async {
    // http://gemscms.aptsys.com.sg/gems_images/Photos/${group_id}/20180713155109.jpg
    WidgetsFlutterBinding.ensureInitialized();
    BaseConfig.httpBase = DefaultHttpManager(
      baseUrl: baseUrl,
      imageUrl: '$baseUrl/gems_images/Photos',
      isDebug: true,
    );
    //设置序列化相关,需要重写，json To From
    BaseConfig.dataBase = DefaultDataBase();
    //页面
    BaseConfig.pageBase = BasePage();
    //实体类序列化
    BaseConfig.entityBase = BaseEntity();
    //Presenter状态码相关
    BaseConfig.presenterBase = BasePresenter();
    /***************不一定要重写***************/
    //对话框
    BaseConfig.dialogBase = BaseDialog();

    eventBus = EventBus();
    //屏幕适配
    ScreenConfig.initConfig(designWidth: 375, designHeight: 812);
    BaseConfig.httpBase.obtainHeaders = obtainGlobalHeaders;
    await initGlobalValue();
    //注册路由
    Routes.registerRouters(providers: [
      RouterGenProviders(),
      OverrideRouteProviders(),
    ]);
    // ReassembleBroadcast().listener(() async {
    //   Routes.registerRouters(providers: [
    //     RouterGenProviders(),
    //     OverrideRouteProviders(),
    //   ]);
    // });
    if (kDebugMode) {
      MockServer.startService();
      BaseConfig.httpBase.addInterceptor(MockInterceptor());
    }
    BaseConfig.httpBase.addInterceptor(LoginInterceptor());
    OneSignPushManager().initialize();
    return false;
  }

  static Map<String, String> obtainGlobalHeaders() {
    // String language = 'en';
    // String systemLanguage = WidgetsBinding.instance.window.locale.toString();
    // // print(systemLanguage);
    // if (systemLanguage.startsWith('zh')) {
    //   language = 'zh';
    // }
    if (Config.userProfile?.locale != null) {
      // language = Config.userProfile.locale;
    }
    return {
      'Accept-Language': Config.languageCode,
      'Accept': 'application/json',
      if (Config?.userProfile?.defaultCompanyId != null)
        'Company-ID': Config?.userProfile?.defaultCompanyId,
      if (Config.token != null) ...{'Authorization': 'Bearer ${Config.token}'},
    };
  }

  static Future<void> initLoginValue([Map<String, dynamic> globalMap]) async {
    globalMap ??= {};
    await _initValue<List<MyInvitationsEntity>>(ConfigKey.myInvitations,
        globalMap, HttpInvitationService.myInvitation(), true);
    if (Config.userProfile?.defaultCompanyId != null) {
      await _initValue<SettingDataEntity>(ConfigKey.settingData, globalMap,
          HttpSettingService.getSettingData(), true);
      await _initValue<CompanyEntity>(
          ConfigKey.currentCompany,
          globalMap,
          HttpCompanyService.showCompanies(Config.userProfile.defaultCompanyId),
          true);
      await _initValue<List<CompanyFinancialYearEntity>>(ConfigKey.currentFYs,
          globalMap, HttpCompanyService.getFinancialYears(), true);
      await _initValue<SettingInfoEntity>(ConfigKey.settingInfo, globalMap,
          HttpSettingService.getSettingInfo(), true);
      await _initValue<List<PaymentTermEntity>>(ConfigKey.paymentTermData,
          globalMap, HttpSettingService.getSettingPaymentTerm(), true);
      await _initValue<RolePermissionEntity>(ConfigKey.rolePermission,
          globalMap, HttpPermissionService.getMyPermission(), true);
      await _initValue<MessageCountEntity>(ConfigKey.msgCountData, globalMap,
          HttpAssistantService.assistantUnRead(), true);
    }
  }

  static Future<void> initNoLoginValue([Map<String, dynamic> globalMap]) async {
    globalMap ??= {};
    if (Config.cashOutCategory == null)
      await _initValue<List<SettingDataCategory>>(ConfigKey.cashOutCategory,
          globalMap, HttpCategoryService.cashOutCategories(), true);
    if (Config.cashInCategory == null)
      await _initValue<List<SettingDataCategory>>(ConfigKey.cashInCategory,
          globalMap, HttpCategoryService.cashInCategories(), true);
    if (Config.expenseCategory == null)
      await _initValue<List<SettingDataCategory>>(ConfigKey.expenseCategory,
          globalMap, HttpCategoryService.expenseCategories(), true);
    if (Config.category == null)
      await _initValue<List<SettingDataCategory>>(ConfigKey.category, globalMap,
          HttpCategoryService.categories(), true);
  }

  static Future<void> initGlobalValue() async {
    String globalJson = await getValue('global');
    Map<String, dynamic> globalMap = {};
    await _initValue<SettingAppEntity>(
        ConfigKey.settingApp, globalMap, HttpSettingService.getSettingApp());
    if (globalJson != null && globalJson != 'null') {
      globalMap = jsonDecode(globalJson);
      await _initValue<String>(ConfigKey.token, globalMap);
      await _initValue<UserProfileEntity>(ConfigKey.userProfile, globalMap);
      await _initValue<bool>(ConfigKey.notFirst, globalMap);
      await _initValue<bool>(ConfigKey.showLogin, globalMap);
      await _initValue<String>(ConfigKey.biometrics, globalMap);
      await _initValue<bool>(ConfigKey.biometricsEnable, globalMap);
      await _initValue<List<int>>(ConfigKey.cashInRecentIds, globalMap);
      await _initValue<List<int>>(ConfigKey.cashOutRecentIds, globalMap);
      await _initValue<List<int>>(ConfigKey.expenseRecentIds, globalMap);
      if (Config.token != null) {
        initLoginValue();
      }
    }
  }

  static Future<void> _initValue<T>(String key, Map<String, dynamic> globalMap,
      [Future<Response> future,
      bool isRefresh = false,
      FutureOr<dynamic> onFormat(Response response)]) async {
    try {
      if (globalMap[key] != null && isRefresh == false) {
        RProvider.myProvider.globalMap[key] =
            BaseConfig.entityBase.formJson<T>(globalMap[key]);
      } else {
        if (globalMap[key] != null) {
          RProvider.myProvider.globalMap[key] =
              BaseConfig.entityBase.formJson<T>(globalMap[key]);
        }
        final connectStatus = await Connectivity().checkConnectivity();
        if (connectStatus == ConnectivityResult.none) {
          throw Exception('not connect network');
        }
        if (future != null) {
          final result = await future;
          RProvider.myProvider.globalMap[key] = onFormat == null
              ? getEntityFromResult<T>(result)
              : onFormat.call(result);
        }
      }
    } catch (e) {
      print(e);
      if (globalMap[key] != null) {
        RProvider.myProvider.globalMap[key] =
            BaseConfig.entityBase.formJson<T>(globalMap[key]);
      }
    }
  }

  //清除配置
  static Future<void> clearConfig() async {
    RProvider.myProvider.globalMap.remove(ConfigKey.settingData);
    RProvider.myProvider.globalMap.remove(ConfigKey.userProfile);
    RProvider.myProvider.globalMap.remove(ConfigKey.myInvitations);
    RProvider.myProvider.globalMap.remove(ConfigKey.currentCompany);
    RProvider.myProvider.globalMap.remove(ConfigKey.currentFYs);
    RProvider.myProvider.globalMap.remove(ConfigKey.employeeMe);
    RProvider.myProvider.globalMap.remove(ConfigKey.token);
    RProvider.myProvider.globalMap.remove(ConfigKey.cashInRecentIds);
    RProvider.myProvider.globalMap.remove(ConfigKey.cashOutRecentIds);
    RProvider.myProvider.globalMap.remove(ConfigKey.expenseRecentIds);
    RProvider.myProvider.globalMap.remove(ConfigKey.msgCountData);
    RProvider.myProvider.globalMap.remove(ConfigKey.paymentTermData);
    RProvider.myProvider.globalMap.remove(ConfigKey.rolePermission);
    RProvider.myProvider.globalMap.remove(ConfigKey.currentSubscription);
    await RProvider.myProvider.updateGlobalMap();
    BaseConfig.httpBase.clearToken();
  }

  static bool get notFirst =>
      RProvider.myProvider.globalMap[ConfigKey.notFirst] ?? false;

  static bool get showLogin =>
      RProvider.myProvider.globalMap[ConfigKey.showLogin] ?? true;

  static SettingAppEntity get settingApp =>
      RProvider.myProvider.globalMap[ConfigKey.settingApp];

  static SettingDataEntity get settingData =>
      RProvider.myProvider.globalMap[ConfigKey.settingData];

  static String get token => RProvider.myProvider.globalMap[ConfigKey.token];

  static UserProfileEntity get userProfile =>
      RProvider.myProvider.globalMap[ConfigKey.userProfile];

  static List<MyInvitationsEntity> get myInvitations =>
      RProvider.myProvider.globalMap[ConfigKey.myInvitations];

  static String get biometrics =>
      RProvider.myProvider.globalMap[ConfigKey.biometrics];

  static SettingInfoEntity get settingInfo =>
      RProvider.myProvider.globalMap[ConfigKey.settingInfo];

  static BiometricsDataEntity get biometricsData => biometrics == null
      ? null
      : BiometricsDataEntity().fromJson(
          json.decode(String.fromCharCodes(base64.decode(Config.biometrics))));

  static set biometricsData(BiometricsDataEntity value) {
    RProvider.myProvider.globalMap[ConfigKey.biometrics] =
        base64.encode(json.encode(value.toJson()).codeUnits);
  }

  static bool get biometricsEnable =>
      RProvider.myProvider.globalMap[ConfigKey.biometricsEnable];

  static CompanyEntity get currentCompany =>
      RProvider.myProvider.globalMap[ConfigKey.currentCompany];

  static List<CompanyFinancialYearEntity> get currentFYs =>
      RProvider.myProvider.globalMap[ConfigKey.currentFYs];

  static CompanyFinancialYearEntity get currentFY =>
      Config.currentFYs?.firstWhere((element) => element.status == 1,
          orElse: () => Config.currentFYs.first);

  static String get pushKey =>
      RProvider.myProvider.globalMap[ConfigKey.pushKey];

  static SettingDataCurrency get defaultCurrency {
    SettingDataCurrency currency;
    if (settingData?.currency != null && settingData.currency.length > 0) {
      try {
        currency = settingData.currency.firstWhere(
            (element) =>
                element.key == Config.currentCompany.defaultCurrencyCode,
            orElse: () => null);
      } catch (e) {}
    }
    return currency;
  }

  static SettingDataTax get noneTax {
    return settingData.tax.firstWhere((element) => element.id == 1,
        orElse: () => settingData.tax.first);
  }

  static CountryEntity get defaultCountry {
    CountryEntity country;
    if (settingData?.country != null && settingData.country.length > 0) {
      try {
        country = settingData.country.firstWhere(
            (element) =>
                element.key?.toLowerCase() == currentCompany.country ??
                'singapore',
            orElse: () => null);
      } catch (e) {}
    }
    return country;
  }

  static const List<String> languages = ['en', 'zh'];

  static String get languageCode {
    return Config.settingInfo?.language ?? 'en';
  }

  static String get myRole => Config.currentCompany == null
      ? ""
      : FormUtils.toUpperCaseFirst(Config.currentCompany.myRole);

  static bool get isStaff => !hasPermission(PermissionUtils.hrPayroll);

  static bool contactIsMe(EmployeeContactEntity paidBy) {
    if (paidBy.id == userProfile.employeeId) {
      return true;
    }
    return false;
  }

  static ImageProvider toImageProvider(String data) {
    if (data == null) return null;
    if (data.startsWith('http')) {
      return NetworkImage(data);
    } else if (data.startsWith('data:image')) {
      String base64String = data.split(',')[1];
      return MemoryImage(base64.decode(base64String));
    } else if (data.startsWith('file') || data.startsWith('/')) {
      return FileImage(File(data));
    }
    return null;
  }

  static ImageProvider localToNetworkProvider(String address, String path) {
    if (address != null) return toImageProvider(address);
    if (path == null) return null;
    return FileImage(File(path));
  }

  static ImageProvider networkToLocalProvider(String address, String path) {
    if (path != null) return FileImage(File(path));
    if (address == null) return null;
    return toImageProvider(address);
  }

  static List<SettingDataCategory> get cashOutCategory =>
      RProvider.myProvider.globalMap[ConfigKey.cashOutCategory];

  static List<SettingDataCategory> get cashInCategory =>
      RProvider.myProvider.globalMap[ConfigKey.cashInCategory];

  static List<SettingDataCategory> get expenseCategory =>
      RProvider.myProvider.globalMap[ConfigKey.expenseCategory];

  static List<SettingDataCategory> get category =>
      RProvider.myProvider.globalMap[ConfigKey.category];

  static CurrentSubscriptionEntity get currentSubscription =>
      RProvider.myProvider.globalMap[ConfigKey.currentSubscription];

  static bool get canAccessAdvanceFeature {
    if (currentSubscription?.plan == null) {
      return true;
    } else {
      if (currentSubscription?.plan?.name == 'starting') {
        return false;
      } else {
        //starting and above can access full feature
        return true;
      }
    }
  }

  static EmployeeContactEntity get _employeeMe =>
      RProvider.myProvider.globalMap[ConfigKey.employeeMe];

  static List<int> get expenseRecentIds =>
      RProvider.myProvider.globalMap[ConfigKey.expenseRecentIds];

  static List<int> get cashInRecentIds =>
      RProvider.myProvider.globalMap[ConfigKey.cashInRecentIds];

  static List<int> get cashOutRecentIds =>
      RProvider.myProvider.globalMap[ConfigKey.cashOutRecentIds];

  static MessageCountEntity get msgCount =>
      RProvider.myProvider.globalMap[ConfigKey.msgCountData];

  static List<PaymentTermEntity> get paymentTerms =>
      RProvider.myProvider.globalMap[ConfigKey.paymentTermData];

  static PaymentTermEntity get paymentTerm => Config.paymentTerms?.firstWhere(
      (element) => element.id == Config.settingInfo?.defaultPaymentTermId,
      orElse: () => null);

  static RolePermissionEntity get myPermission =>
      RProvider.myProvider.globalMap[ConfigKey.rolePermission];

  static bool hasPermission(String key) {
    return Config.myPermission?.allPermissions
                ?.firstWhere((element) => element.key == key,
                    orElse: () => PermissionEntity())
                ?.checked ==
            1 ??
        false;
  }

  static Future<void> refreshMessageCount() async {
    await _initValue<MessageCountEntity>(ConfigKey.msgCountData, {},
        HttpAssistantService.assistantUnRead(), true);
    RProvider.myProvider.updateGlobalMap();
  }

  static Future<EmployeeContactEntity> getEmployeeMe(BaseProvider provider,
      [bool isShowDialog = true, bool isAllwayRefresh = false]) async {
    if (_employeeMe == null || isAllwayRefresh == true) {
      await provider.post(HttpEmployeeContactService.contactList(),
          autoHandler: AutoHandler.Toast,
          loadingTip: isShowDialog == true ? S.current.loading : null,
          onSuccess: (result) {
        final list = getEntityFromResult<List<EmployeeContactEntity>>(result);
        RProvider.myProvider.globalMap[ConfigKey.employeeMe] = list.firstWhere(
            (element) => element.userId == userProfile.id,
            orElse: () => null);
      });
    }
    return _employeeMe;
  }
}

class ConfigKey {
  //登录信息
  static final String loginInfo = 'loginInfo';

  static final String notFirst = 'notFirst';

  static final String showLogin = 'showLogin';

  static final String settingData = 'settingData';

  static final String settingApp = 'settingApp';

  static final String token = 'token';

  static final String userProfile = 'userProfile';

  static final String myInvitations = 'myInvitations';

  static final String biometrics = 'biometrics';

  static final String biometricsEnable = 'biometricsEnable';

  static final String currentCompany = 'currentCompany';

  static final String currentFYs = 'currentFYs';

  static final String pushKey = 'pushKey';

  static final String settingInfo = 'settingInfo';

  static final String cashOutCategory = 'cashOutCategory';

  static final String cashInCategory = 'cashInCategory';

  static final String expenseCategory = 'expenseCategory';

  static final String category = 'category';

  static final String employeeMe = 'employeeMe';

  static final String currentSubscription = 'currentSubscription';

  static final String expenseRecentIds = 'expenseRecentIds';

  static final String cashInRecentIds = 'cashInRecentIds';

  static final String cashOutRecentIds = 'cashOutRecentIds';

  static final String msgCountData = 'msgCountData';

  static final String paymentTermData = 'paymentTermData';

  static final String rolePermission = 'rolePermission';
}
